﻿using Furion;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using HYHY_Core.Entities.User;
using HYHY_Core.Enums;
using HYHY_Core.Tool.Service;
using HYHY_Core.Tool.Tool;
using HYQY_App.Login.Interface;
using HYQY_App.Tool.GenerateId;
using HYQY_Core.Entities.User;
using HYQY_Core.Entities.Utils;
using HYQY_Core.Entities.Ware;
using MailKit.Net.Smtp;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using MimeKit;
using SqlSugar;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using static HYQY_App.Login.Dtos.LoginDto;

namespace HYQY_App.Login.Service
{
    /// <summary>
    /// 登录服务
    /// </summary>
    [ApiDescriptionSettings(ApiGroupEnumsCode.Login, Name = "LoginService")]
    public class LoginService : IDynamicApiController, ITransient, ILoginService
    {
        /// <summary>
        /// sql
        /// </summary>
        private readonly ISqlSugarClient _db;


        /// <summary>
        /// 生成器
        /// </summary>
        private readonly IGenerateIdService _generateIdService;

        /// <summary>
        /// 刷新Token有效期
        /// </summary>
        private static int _refreshExpiredTime;

        /// <summary>
        /// Http上下文
        /// </summary>
        private readonly IHttpContextAccessor _httpContextAccessor;

        /// <summary>
        /// Token有效期
        /// </summary>
        private static long _expiredTime;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="db"></param>
        public LoginService(ISqlSugarClient db, IGenerateIdService generateIdService, IHttpContextAccessor httpContextAccessor)
        {
            _db = db;
            _generateIdService = generateIdService;
            _httpContextAccessor = httpContextAccessor;
            _expiredTime = Convert.ToInt64(App.Configuration["JWTSettings:ExpiredTime"]);
            _refreshExpiredTime = Convert.ToInt32(App.Configuration["JWTSettings:RefreshExpiredTime"]);
        }

        /// <summary>
        /// 发送邮箱验证码（QQ、163网易）
        /// </summary>
        /// <returns></returns>
        public async Task<ResponseStatus> SendEmail(SendEmailAsyncInput input)
        {
            // 1. 校验邮箱格式
            if (!Regex.IsMatch(input.Email, @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"))
            {
                throw Oops.Bah("邮箱格式无效");
            }

            // 获取邮箱@符号后面的字符
            string domain = GetEmailDomain(input.Email);

            static string GetEmailDomain(string email)
            {
                int atIndex = email.IndexOf('@');
                if (atIndex >= 0 && atIndex < email.Length - 1)
                {
                    return email.Substring(atIndex + 1);
                }
                return string.Empty;
            }

            // 调用qq邮箱
            if (domain == "qq.com")
            {
                await QQEmail(new SendEmailAsyncInput
                {
                    AccountNumber = input.AccountNumber,
                    Email = input.Email,
                });
            } // 网易邮箱
            else if (domain == "163.com")
            {
                await NeteaseEmail(new SendEmailAsyncInput
                {
                    AccountNumber = input.AccountNumber,
                    Email = input.Email,
                });
            }


            // 发送成功返回信息和状态
            return new ResponseStatus { Message = "邮件发送成功，5分钟内有效", StatusCode = 200 };
        }

        /// <summary>
        /// QQ 邮箱
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task QQEmail(SendEmailAsyncInput input)
        {
            // 1. 创建邮件内容
            var message = new MimeMessage();
            // 发件人：名称自定义，邮箱填你的 QQ 邮箱（已开启 SMTP）
            message.From.Add(new MailboxAddress("花漾轻语后台-HYQY", "1460226134@qq.com"));
            // 收件人：名称自定义，这里填注册人邮箱
            message.To.Add(new MailboxAddress(input.AccountNumber, input.Email));
            message.Subject = "注册验证码"; // 邮件主题

            // 随机生成验证码
            var code = RandomNumberGenerator.GetInt32(100000, 999999).ToString();

            // 邮件正文（纯文本示例，也可改成 HTML 格式）
            message.Body = new TextPart("plain")
            {
                Text = $"您的验证码是：{code}"
            };

            // 2. 连接 SMTP 服务器并发送（以 QQ 邮箱为例，需替换授权码）
            using var client = new SmtpClient();
            try
            {
                // QQ 邮箱 SMTP 服务器地址和端口，其他邮箱需对应修改
                await client.ConnectAsync("smtp.qq.com", 587, MailKit.Security.SecureSocketOptions.StartTls);

                // 发件人认证：填发件人邮箱和 SMTP 授权码（不是 QQ 邮箱登录密码！）
                // 需在 QQ 邮箱设置里开启 SMTP 服务，获取授权码替换下面的 "your-authorization-code"
                await client.AuthenticateAsync("1460226134@qq.com", "mmbeofglrnpwgjga");

                // 发送邮件
                await client.SendAsync(message);

                // 断开连接
                await client.DisconnectAsync(true);

                Console.WriteLine("邮件发送成功！5分钟内有效");

                // 添加邮箱信息
                int count = await _db.Insertable(new VerificationCodeInfo
                {
                    Id = Guid.NewGuid().ToString("N"),
                    VerificationCode = code,
                    Type = "QQ",
                    MailBox = input.Email,
                    IsUse = false,
                    CreateMan = input.AccountNumber,
                    CreateTime = DateTime.Now,
                }).ExecuteCommandAsync();

                if (count < 1)
                {
                    Oops.Bah("邮箱新增失败==>SendEmailAsync");
                }

            }
            catch (Exception ex)
            {
                Oops.Bah($"邮件发送失败：{ex.Message}");
                Console.WriteLine($"邮件发送失败：{ex.Message}");

                // 确保连接已断开
                if (client.IsConnected)
                {
                    await client.DisconnectAsync(true);
                }
            }
        }

        /// <summary>
        /// 网易邮箱（有效期180天，需要重新获取授权码————2025 年 12 月 18 日）
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task NeteaseEmail(SendEmailAsyncInput input)
        {
            // 1. 创建邮件内容
            var message = new MimeMessage();
            // 发件人：名称自定义，邮箱填你的 QQ 邮箱（已开启 SMTP）
            message.From.Add(new MailboxAddress("花漾轻语后台-HYQY", "15078157002@163.com"));
            // 收件人：名称自定义，这里填注册人邮箱
            message.To.Add(new MailboxAddress(input.AccountNumber, input.Email));
            message.Subject = "注册验证码"; // 邮件主题

            // 随机生成验证码
            var code = RandomNumberGenerator.GetInt32(100000, 999999).ToString();

            // 邮件正文（纯文本示例，也可改成 HTML 格式）
            message.Body = new TextPart("plain")
            {
                Text = $"您的验证码是：{code}"
            };

            // 2. 连接 SMTP 服务器并发送（以 网易 邮箱为例，需替换授权码）
            using var client = new SmtpClient();
            try
            {
                // 网易 邮箱 SMTP 服务器地址和端口，其他邮箱需对应修改
                await client.ConnectAsync("smtp.163.com", 465, MailKit.Security.SecureSocketOptions.SslOnConnect);

                // 发件人认证：填发件人邮箱和 SMTP 授权码（有效期180天，需要重新获取授权码————2025 年 12 月 18 日）
                // 需在 网易 邮箱设置里开启 SMTP 服务，获取授权码替换下面的 "your-authorization-code"
                await client.AuthenticateAsync("15078157002@163.com", "NWvQJEyzLLNCfrUP");

                // 发送邮件
                await client.SendAsync(message);

                // 断开连接
                await client.DisconnectAsync(true);

                Console.WriteLine("邮件发送成功！5分钟内有效");

                // 添加邮箱信息
                int count = await _db.Insertable(new VerificationCodeInfo
                {
                    Id = Guid.NewGuid().ToString("N"),
                    VerificationCode = code,
                    Type = "163网易",
                    MailBox = input.Email,
                    IsUse = false,
                    CreateMan = input.AccountNumber,
                    CreateTime = DateTime.Now,
                }).ExecuteCommandAsync();

                if (count < 1)
                {
                    Oops.Bah("邮箱新增失败==>SendEmailAsync");
                }

            }
            catch (Exception ex)
            {
                Oops.Bah($"邮件发送失败：{ex.Message}");
                Console.WriteLine($"邮件发送失败：{ex.Message}");

                // 确保连接已断开
                if (client.IsConnected)
                {
                    await client.DisconnectAsync(true);
                }
            }
        }

        /// <summary>
        /// 后台用户注册
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AllowAnonymous]
        public async Task<ResponseStatus> CreateUser(RegisterIndexInput input)
        {
            //去掉入参前后空格
            ToolHelper.TrimStrings(input);

            //if (string.IsNullOrWhiteSpace(input.UserName))
            //{ throw Oops.Bah("用户名不能为空"); }
            if (string.IsNullOrWhiteSpace(input.AccountNumber))
            { throw Oops.Bah("账号不能为空"); }
            if (string.IsNullOrWhiteSpace(input.Password))
            { throw Oops.Bah("密码不能为空"); }
            if (input.Password.Length < 6)
            { throw Oops.Bah("密码长度不能小于6个字符"); }
            // 匹配QQ邮箱和网易邮箱的正则表达式
            string emailRegex = @"^(?:\d{5,11}|[a-zA-Z0-9._%+-]{1,64})@(?:qq\.com|163\.com)$";
            if (!Regex.IsMatch(input.Email, emailRegex))
            {
                throw Oops.Bah("目前仅支持QQ、163网易邮箱哦！");
            }

            // 验证邮箱号是否被注册
            var platformUserEmail = await _db.Queryable<PlatformUser>()
                .FirstAsync(it => it.Email == input.Email);

            if (platformUserEmail != null)
            {
                throw Oops.Bah($"该邮箱号已被注册，账号为：【{platformUserEmail.AccountNumber}】");
            }

            // 往前推五分钟，AddMinutes 的参数为负数表示往前推
            DateTime fiveMinutesAgo = DateTime.Now.AddMinutes(-5);
            var email = await _db.Queryable<VerificationCodeInfo>()
                .FirstAsync(it => it.VerificationCode == input.VerificationCode) ?? throw Oops.Bah("验证码错误");

            if (email.CreateTime <= fiveMinutesAgo)
            {
                throw Oops.Bah("验证码已失效，请重新发送，仅在5分钟内有效");
            }
            email.IsUse = true;

            // 获取用户id
            var userId = await _generateIdService.CreateUserId(input.AccountNumber);
            userId = "P" + userId;

            // guid
            var guid = Guid.NewGuid().ToString("N");
            // SHA-256加密
            string hashedPassword = PasswordHasher.HashPassword(input.Password);

            // 账号管理表
            int count = 0;
            var account = new Account()
            {
                AId = guid,
                UserId = userId,
                //Phone = input.Phone,
                LoginCount = 0,
                AccountNumber = input.AccountNumber,
                // 加密后密码
                Password = hashedPassword,
                LastLoginTime = DateTime.Now,
                UnencryptedPad = input.Password,
                //获取枚举定义
                UserType = EnumCommon.GetDescription((UserTypeEnumsCode)0),
                CreateMan = input.AccountNumber,
                CreateTime = DateTime.Now,
                IsDelete = false,
            };

            // 平台
            var platformUser = new PlatformUser()
            {
                AId = guid,
                //Phone = input.Phone,
                PlatformUserId = userId,
                AccountNumber = input.AccountNumber,
                Email = input.Email,
                WeChat = input.WeChat,
                IsDelete = false,
                CreateMan = input.AccountNumber,
                CreateTime = DateTime.Now,

            };

            count = await _db.Insertable(platformUser).ExecuteCommandAsync();
            count += await _db.Insertable(account).ExecuteCommandAsync();

            if (count != 2)
            {
                throw Oops.Bah("注册异常");
            }

            // 修改验证码为已使用
            await _db.Updateable(email).WhereColumns(it => it.Id).UpdateColumns(it => it.IsUse).ExecuteCommandAsync();

            return new ResponseStatus { Message = "注册成功", StatusCode = 200 };
        }

        /// <summary>
        /// 后台获取平台Token
        /// </summary>
        /// <param name="user"></param>
        /// <param name="accountInfo"></param>
        /// <returns></returns>
        [ApiDescriptionSettings(false)]
        public WebLoginResp GetTokenOfWeb(PlatformUser user, Account accountInfo)
        {
            Claim[] claims = new[]
            {
                new Claim( "UserId", user.PlatformUserId ),
                //new Claim("UserName", user.UserName),
                new Claim( "DataPermissions", user.Email ),
                new Claim( "AccountNumber", user.AccountNumber ),
                // 此处应该使用  accountInfo.PermissionGroupId
                new Claim( "PermissionGroupId","0BA67BFC0080B3CD27FC1532AD35408E"),
                new Claim( "UserType", EnumCommon.GetDescription((UserTypeEnumsCode)0)),
                new Claim( "UserName", user. AccountNumber)
            };

            // 准备key
            SymmetricSecurityKey key = new((Encoding.UTF8.GetBytes(App.Configuration["JWTSettings:IssuerSigningKey"])));

            // HS256加密
            SigningCredentials signing = new(key, SecurityAlgorithms.HmacSha256);

            // 将long转换为分钟
            // 默认过期时间20分钟、实际Token过期时间以配置文件为主
            var exp = TimeSpan.FromMilliseconds(_expiredTime).TotalMinutes;

            JwtSecurityToken token = new(
                issuer: App.Configuration["JWTSettings:ValidIssuer"],
                audience: App.Configuration["JWTSettings:ValidAudience"],
                claims: claims,
                expires: DateTime.Now.AddMinutes(exp),
                signingCredentials: signing);

            // 获取token
            string accessToken = new JwtSecurityTokenHandler().WriteToken(token);

            var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, _refreshExpiredTime);

            // 添加到请求头
            _httpContextAccessor.HttpContext.Response.Headers["Access-Control-Expose-Headers"] = "access-token,x-access-token";
            _httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
            _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;

            return new WebLoginResp
            {
                //UserName = user.AccountNumber,
                AccessToken = accessToken,
                //ExpiredTime = exp,
                UserId = user.PlatformUserId,
                SigninedTime = DateTimeOffset.Now,
                LoginCount = accountInfo.LoginCount,
                DataPermissions = user.PlatformUserId,
                AccountNumber = accountInfo.AccountNumber,
                UserTypeText = accountInfo.UserType,
                UserType = (int)UserTypeEnumsCode.Platform,
                PermissionGroupId = "0BA67BFC0080B3CD27FC1532AD35408E",
                LastLoginTime = accountInfo.LastLoginTime == null ? "" : accountInfo.LastLoginTime.Value.ToString("yyyy-MM-dd HH:mm:ss"),
            };
        }

        /// <summary>
        /// 后台用户登录
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AllowAnonymous]
        public async Task<WebLoginResp> LogIn(LogInInput input)
        {
            //去掉入参前后空格
            ToolHelper.TrimStrings(input);
            input.UserType = 0;
            if (string.IsNullOrWhiteSpace(input.AccountNumber))
            { throw Oops.Bah("账号不能为空"); }
            if (string.IsNullOrWhiteSpace(input.Password))
            { throw Oops.Bah("密码不能为空"); }
            if (!input.UserType.HasValue)
            { throw Oops.Bah("请选择登录类型"); }

            // SHA-256加密
            string hashedPassword = PasswordHasher.HashPassword(input.Password);

            var platform = await _db.Queryable<PlatformUser>()
                .FirstAsync(it =>
                it.AccountNumber == input.AccountNumber || it.Email == input.AccountNumber)
                ?? throw Oops.Bah("未注册平台账号");

            var account = await _db.Queryable<Account>()
                .FirstAsync(it => it.UserId == platform.PlatformUserId)
                ?? throw Oops.Bah("未注册账号");

            WebLoginResp output = new();

            output.AccountNumber = account.AccountNumber;
            switch (input.UserType)
            {
                // 平台
                case 0:

                    // 获取token
                    output = GetTokenOfWeb(platform, account);
                    output.Email = platform.Email;
                    output.WeChat = platform.WeChat;
                    output.HeadPortraitUrl = platform.HeadPortraitUrl;
                    output.CreateTime = platform.CreateTime;
                    break;
                default:
                    break;
            }

            if (account.AccountNumber != input.AccountNumber && platform.Email != input.AccountNumber)
            {
                throw Oops.Bah("账号不正确");
            }
            if (account.UnencryptedPad != input.Password || account.Password != hashedPassword)
            {
                throw Oops.Bah("密码不正确");
            }

            account.LoginCount += 1;
            account.LastLoginTime = DateTime.UtcNow;
            await _db.Updateable(account).WhereColumns(it => it.AId)
                .UpdateColumns(it => new
                {
                    it.LoginCount,
                    it.LastLoginTime,
                }).ExecuteCommandAsync();
            output.StatusCode = 200;
            output.Message = "登录成功！";
            return output;
        }

        /// <summary>
        /// 获取小程序Token
        /// </summary>
        /// <param name="user"></param>
        /// <param name="accountInfo"></param>
        /// <returns></returns>
        [ApiDescriptionSettings(false)]
        public string GetAppletToken(UserInfo user)
        {
            Claim[] claims = new[]
            {
                new Claim( "OpenId", user.OpenId ),
                //new Claim("UserName", user.UserName),
                new Claim( "Session_key", user.Session_key ),
            };

            // 准备key
            SymmetricSecurityKey key = new((Encoding.UTF8.GetBytes(App.Configuration["JWTSettings:IssuerSigningKey"])));

            // HS256加密
            SigningCredentials signing = new(key, SecurityAlgorithms.HmacSha256);

            // 定义 Token 过期天数（这里设为 7 天）
            int expireDays = 7;

            JwtSecurityToken token = new(
                issuer: App.Configuration["JWTSettings:ValidIssuer"],
                audience: App.Configuration["JWTSettings:ValidAudience"],
                claims: claims,
                expires: DateTime.Now.AddDays(expireDays), // 使用变量控制过期天数
                signingCredentials: signing);

            // 获取token
            string accessToken = new JwtSecurityTokenHandler().WriteToken(token);

            return accessToken;
        }

        /// <summary>
        /// 小程序用户授权登录
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [AllowAnonymous]
        public async Task<AppletLoginResp> AppletLogin(AppletLoginInput input)
        {
            //去掉入参前后空格
            ToolHelper.TrimStrings(input);

            if (string.IsNullOrWhiteSpace(input.OpenId))
            {
                throw Oops.Bah("授权登录OpenId不能为空");
            }
            if (string.IsNullOrWhiteSpace(input.Session_key))
            {
                throw Oops.Bah("授权登录Session_key不能为空");
            }

            var user = await _db.Queryable<UserInfo>()
                .FirstAsync(it => it.OpenId == input.OpenId);

            var output = new AppletLoginResp();
            // 注册
            if (user == null)
            {
                // 获取用户id
                var userId = await _generateIdService.CreateUserId(input.OpenId);
                userId = "Y" + userId;

                int count = await _db.Insertable(new UserInfo
                {
                    OpenId = input.OpenId,
                    Session_key = input.Session_key,
                    UserId = userId,
                    AvatarUrl = input.AvatarUrl,
                    UserName = input.UserName,
                    CreateMan = input.OpenId,
                    CreateTime = DateTime.Now,
                    IsDelete = false
                }).ExecuteCommandAsync();
                if (count < 1)
                {
                    throw Oops.Bah("首次登录，注册失败！=>【AppletLogin】");
                }

                user = await _db.Queryable<UserInfo>()
                .FirstAsync(it => it.OpenId == input.OpenId);
            }

            output.UserName = user.UserName;
            output.AvatarUrl = user.AvatarUrl;
            output.OpenId = user.OpenId;
            output.Session_key = user.Session_key;
            output.StatusCode = 200;
            output.Message = "登录成功！";
            output.Token = GetAppletToken(user);

            return output;
        }
    }
}
